//#define DebugDeserialization

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.Xml;
using System.Collections;
using System.Reflection;
using System.Security.Permissions;
using System.ComponentModel;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Data;
using System.Data.Odbc;
using NumericalRecipes;
using FftLib;
using BmgNet;
using StaticUtility;

namespace ObjectLib
{
    /// <summary>
    /// [Serializable] : CollectionBase
    /// A class for polygon vertices.
    /// </summary>
    [Serializable]
    public class VertexF: ISerializable,IComparable,IXmlSerializable
    {
        /// <summary>
        /// Horizontal location of vertex.
        /// </summary>
        public float X;
        /// <summary>
        /// Vertical location of vertex.
        /// </summary>
        public float Y;
		public PointF PointF
		{
			get
			{
				return new PointF(X,Y);
			}
			set
			{
				X = value.X;
				Y = value.Y;
			}
		}
        [NonSerialized]
        public int SerializationPointer = -1;
        /// <summary>
        /// A list of PolygonF objects which claim ownership over this VertexF.
        /// </summary>
        [NonSerialized]
        public PolyFarray Polygons;
        /// <summary>
        /// The contours which own this VertexF
        /// </summary>
        [NonSerialized]
        public ContourFarray Contours;
        public VertexF()
        {
            this.Polygons = new PolyFarray(true);
            this.Contours = new ContourFarray(true);
        }
        public VertexF(float x,float y,PolyFarray Polygons,ContourFarray Contours)
        {
            X = x;
            Y = y;
            this.Polygons = Polygons;
            this.Contours = Contours;
        }
        public VertexF(PointF pt,PolyFarray Polygons,ContourFarray Contours)
        {
            X = pt.X;
            Y = pt.Y;
            this.Polygons = Polygons;
            this.Contours = Contours;
        }
        #region Serialization
        public VertexF(SerializationInfo info,StreamingContext context)
        {
#if DebugDeserialization
            ObjectParserData.DbgMsg = "Begin VertexF";
#endif
            X = info.GetSingle("X");
            Y = info.GetSingle("Y");
            this.Polygons = new PolyFarray(true);
            this.Contours = new ContourFarray(true);
#if DebugDeserialization
            ObjectParserData.DbgMsg = "End VertexF";
#endif
        }
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("X",X);
            info.AddValue("Y",Y);
            this.Polygons = new PolyFarray(true);
            this.Contours = new ContourFarray(true);
        }
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
            /*
            System.Xml.Schema.XmlSchema output = new System.Xml.Schema.XmlSchema();
            System.Xml.Schema.XmlSchemaElement el;

            el = new System.Xml.Schema.XmlSchemaElement();
            el.Name = "Id";
            el.SchemaType = System.Xml.Schema.XmlSchemaType.GetBuiltInSimpleType( System.Xml.Schema.XmlTypeCode.Id );
            output.Items.Add(el);

            el = new System.Xml.Schema.XmlSchemaElement();
            el.Name = "X";
            el.SchemaType = System.Xml.Schema.XmlSchemaType.GetBuiltInSimpleType(System.Xml.Schema.XmlTypeCode.Float);
            output.Items.Add(el);

            el = new System.Xml.Schema.XmlSchemaElement();
            el.Name = "Y";
            el.SchemaType = System.Xml.Schema.XmlSchemaType.GetBuiltInSimpleType(System.Xml.Schema.XmlTypeCode.Float);
            output.Items.Add(el);

            return output;
            */
        }
        public void ReadXml(System.Xml.XmlReader reader)
        {
            int depth = reader.Depth;
            bool reading=false;
            while( (reading || reader.Read()) && reader.Depth>depth)
            {
                if((reader.NodeType == XmlNodeType.Element || reader.NodeType==XmlNodeType.Text) && reader.Name!="")
                {
                    reading = true;
                    switch(reader.Name)
                    {
                        case "Id": { this.SerializationPointer=reader.ReadElementContentAsInt(); } break;
                        case "X": { this.X=reader.ReadElementContentAsFloat(); } break;
                        case "Y": { this.Y=reader.ReadElementContentAsFloat(); } break;
                        default: throw new Exception("Unexpected field encounterd while reading XML for a VertexF object.  Field name = " + reader.Name);
                    }
                }
            }
        }
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteStartElement("Vertex");
            writer.WriteElementString("Id", SerializationPointer.ToString());
            writer.WriteElementString("X", X.ToString());
            writer.WriteElementString("Y", Y.ToString());
            writer.WriteEndElement();
        }
        #endregion
        public void AddOwner(PathF path)
        {
            if(path.GetType()==typeof(PolygonF))
                Polygons.Add(path);
            else
                Contours.Add(path);
        }
        public void RemoveOwner(PathF path)
        {
            if(path.GetType()==typeof(PolygonF))
                Polygons.Remove(path);
            else
                Contours.Remove(path);
        }
        /// <summary>
        /// Removes all of this VertexF owners from internal variables 'Polygons' and 'Contours' by reinitializing them.
        /// The old lists are returned as 'polygons' and 'contours'.
        /// </summary>
        /// <param name="polygons">The old value of 'Polygons'.</param>
        /// <param name="contours">The old value of 'Contours'.</param>
        public void RemoveAllOwners(out PolyFarray polygons,out ContourFarray contours)
        {
            polygons = this.Polygons;
            contours = this.Contours;
            this.Polygons = new PolyFarray(true);
            this.Contours = new ContourFarray(true);
        }
        /// <summary>
        /// Removes this VertexF from the specified path.
        /// </summary>
        /// <param name="path"></param>
        /// <returns>The integer index into the path where the object was removed.</returns>
        public int RemoveFromOwner(PathF path)
        {
            int output = path.IndexOf(this);
            path.RemoveAt(output);
            return output;
        }
        /// <summary>
        /// Executes the function 'RemoveFromOwner' for every path in 'Polygons' and 'Contours'.
        /// </summary>
        /// <returns>The result of 'RemoveFromOwner' for every call, the 'Polygons' are called before 'Contours' in list order.</returns>
        public void RemoveFromAllOwners(out int[] polyIndices,out int[] conIndices)
        {
            int i;
            polyIndices = new int[this.Polygons.Count];
            conIndices = new int[this.Contours.Count];

            for(i=0;i<Polygons.Count;i++)
            {
                polyIndices[i] = Polygons[i].IndexOf(this);
                Polygons[i].RemoveAt(polyIndices[i]);
            }
            for(i=0;i<Contours.Count;i++)
            {
                conIndices[i] = Contours[i].IndexOf(this);
                Contours[i].RemoveAt(conIndices[i]);
            }
        }
        public void MimicValuesOf(VertexF vf)
        {
            if(vf != null)
            {
                X = vf.X;
                Y = vf.Y;
                Polygons = vf.Polygons;
                Contours = vf.Contours;
            }
        }
        public override string ToString()
        {
            return "[" + Convert.ToString(Math.Round(X,1))
				+ ", " + Convert.ToString(Math.Round(Y,1)) + "]";
        }
        public string Name
        {
            get
            {
                return ToString();
            }
        }

        /// <summary>
        /// After a VertexF object is deleted, the OwnedEdges of the parent polygons are updated.
        /// </summary>
        /// <param name="oldEdges">EdgeArray  Contents are destroyed.  Returns any Edge objects
        /// that were removed from their owners.</param>
        /// <param name="oldIndices">int[]  The indices of this object within its polygon owners.</param>
        public void AfterDelete_UpdateEdges(EdgeArray oldEdges,int[] oldIndices)
        {
            //	make sure the contents are cleared.
            oldEdges.Clear();
            int i,j;
            PolygonF pf;
            Edge fe;
            //	for each polygon in the set of owners
            for(i=0;i<this.Polygons.Count;i++)
            {
                pf = this.Polygons[i];
                //	temporary insert
                pf.Insert(oldIndices[i],this);
                //	for each owned edge in the polygon
                for(j=0;j<pf.OwnedEdges.Count;j++)
                {
                    fe = pf.OwnedEdges[j];
                    //	if the full edge owns the vertex.
                    if(fe.OwnsIndex(oldIndices[i]))
                    {
                        //	remove it and keep track in oldEdges.
                        oldEdges.Add(fe);
                        pf.OwnedEdges.RemoveAt(j);
                        break;
                    }
                }
                //	remove temporary insert
                pf.RemoveAt(oldIndices[i]);
            }
        }
        /// <summary>
        /// Gets the rectangle that surrounds the PointF (with extra cushion).
        /// </summary>
        /// <param name="cushion">float  Pushes each vertex of the rectangle out by this extra amount.</param>
        /// <returns>RectangleF  The bounding rectangle with vertices pushed out by the amount of cushion.</returns>
        public static RectangleF GetBoundingRectF(PointF p,float cushion)
        {
            float l,r,t,b;
            l=r=p.X;
            t=b=p.Y;
            return new RectangleF(l-cushion,t-cushion,r-l+1.0f+2.0f*cushion,b-t+1.0f+2.0f*cushion);
        }
        public static RectangleF GetBoundingRectF(PointF pt1,PointF pt2,float cushion)
        {
            float l,t,r,b;
            l = pt1.X;
            r = pt1.X;
            t = pt1.Y;
            b = pt1.Y;
            if(pt2.X < l) l = pt2.X;
            if(pt2.X > r) r = pt2.X;
            if(pt2.Y < t) t = pt2.Y;
            if(pt2.Y > b) b = pt2.Y;
            return new RectangleF(l-cushion,t-cushion,r-l+1+2.0f*cushion,b-t+1+2.0f*cushion);
        }
        public static RectangleF GetBoundingRectF(VertexF pt1,VertexF pt2,float cushion)
        {
            float l,t,r,b;
            l = pt1.X;
            r = pt1.X;
            t = pt1.Y;
            b = pt1.Y;
            if(pt2!=null)
            {
                if(pt2.X < l) l = pt2.X;
                if(pt2.X > r) r = pt2.X;
                if(pt2.Y < t) t = pt2.Y;
                if(pt2.Y > b) b = pt2.Y;
            }
            return new RectangleF(l-cushion,t-cushion,r-l+1.0f+2.0f*cushion,b-t+1.0f+2.0f*cushion);
        }
        public static Rectangle GetBoundingRect(PointF pt1,PointF pt2,float cushion)
        {
            return ToBoundingRect(GetBoundingRectF(pt1,pt2,cushion));
        }
        public static Rectangle GetBoundingRect(VertexF pt1,VertexF pt2,float cushion)
        {
            return ToBoundingRect(GetBoundingRectF(pt1,pt2,cushion));
        }
        public static Rectangle ToBoundingRect(RectangleF rf)
        {
            return new Rectangle((int)rf.Left,(int)rf.Top,(int)(rf.Right-rf.Left)+3,(int)(rf.Bottom-rf.Top)+3);
        }
        public static Rectangle GetBoundingRect(VertexF pt1,float cushion)
        {
            return ToBoundingRect(GetBoundingRectF(pt1,cushion));
        }
        public static RectangleF GetBoundingRectF(VertexF pt1,float cushion)
        {
            float l,r,t,b;
            l=r=pt1.X;
            t=b=pt1.Y;
            return new RectangleF(l-cushion,t-cushion,r-l+1.0f+2.0f*cushion,b-t+1.0f+2.0f*cushion);
        }

        /// <summary>
        /// Gets the rectangle that surrounds the PointF (with extra cushion).
        /// </summary>
        /// <param name="cushion">float  Pushes each vertex of the rectangle out by this extra amount.</param>
        /// <returns>Rectangle  The bounding rectangle with vertices pushed out by the amount of cushion.  This will be slightly larger
        /// than the rectangle returned by GetBoundingRectF because rounding insures that at least the GetBoundingRectF will be surrounded.</returns>
        public static Rectangle GetBoundingRect(Point p,float cushion)
        {
            RectangleF rf = GetBoundingRectF(new PointF(p.X,p.Y),cushion);
            int l = (int)rf.Left;
            int r = (int)(rf.Right+1.0f);
            int t = (int)rf.Top;
            int b = (int)(rf.Bottom+1.0f);
            return new Rectangle(l-(int)cushion,t-(int)cushion,r-l+2+2*(int)(cushion+1.0f),b-t+2+2*(int)(cushion+1.0f));
        }
        public static Rectangle GetBoundingRect(PointF p,float cushion)
        {
            RectangleF rf = GetBoundingRectF(p,cushion);
            int l = (int)rf.Left;
            int r = (int)(rf.Right+1.0f);
            int t = (int)rf.Top;
            int b = (int)(rf.Bottom+1.0f);
            return new Rectangle(l-(int)cushion,t-(int)cushion,r-l+2+2*(int)(cushion+1.0f),b-t+2+2*(int)(cushion+1.0f));
        }
        public static Rectangle GetBoundingRect(PointF[] pts,float cushion)
        {
            int l,t,r,b;
            l = (int)(pts[0].X);
            r = (int)(pts[0].X+1.0f);
            t = (int)(pts[0].Y);
            b = (int)(pts[0].Y+1.0f);
            for(int i=1;i<pts.Length;i++)
            {
                if((int)(pts[i].X) < l) l = (int)(pts[i].X);
                if((int)(pts[i].X+1.0f) > r) r = (int)(pts[i].X+1.0f);
                if((int)(pts[i].Y) < t) t = (int)(pts[i].Y);
                if((int)(pts[i].Y+1.0f) > b) b = (int)(pts[i].Y+1.0f);
            }
            return new Rectangle(l-(int)cushion,t-(int)cushion,r-l+2+2*(int)(cushion+1.0f),b-t+2+2*(int)(cushion+1.0f));
        }
        public static Rectangle GetBoundingRect(VertexF[] pts,float cushion)
        {
            int l,t,r,b;
            l = (int)(pts[0].X);
            r = (int)(pts[0].X+1.0f);
            t = (int)(pts[0].Y);
            b = (int)(pts[0].Y+1.0f);
            for(int i=1;i<pts.Length;i++)
            {
                if((int)(pts[i].X) < l) l = (int)(pts[i].X);
                if((int)(pts[i].X+1.0f) > r) r = (int)(pts[i].X+1.0f);
                if((int)(pts[i].Y) < t) t = (int)(pts[i].Y);
                if((int)(pts[i].Y+1.0f) > b) b = (int)(pts[i].Y+1.0f);
            }
            return new Rectangle(l-(int)cushion,t-(int)cushion,r-l+2+2*(int)(cushion+1.0f),b-t+2+2*(int)(cushion+1.0f));
        }
        public static Rectangle GetBoundingRect(Point pt1,Point pt2,float cushion)
        {
            int l,t,r,b;
            l = (int)(pt1.X);
            r = (int)(pt1.X);
            t = (int)(pt1.Y);
            b = (int)(pt1.Y);
            if(pt2.X < l) l = pt2.X;
            if(pt2.X > r) r = pt2.X;
            if(pt2.Y < t) t = pt2.Y;
            if(pt2.Y > b) b = pt2.Y;
            return new Rectangle(l-(int)cushion,t-(int)cushion,r-l+2+2*(int)(cushion+1.0f),b-t+2+2*(int)(cushion+1.0f));
        }

        public static Rectangle GetBoundingRect(Rectangle rect,VertexF vert,float cushion)
        {
            int l = rect.Left;
            int r = rect.Right;
            int t = rect.Top;
            int b = rect.Bottom;

            if(vert.X < l) l = (int)vert.X;
            if(vert.X > r) r = (int)vert.X+1;
            if(vert.Y < t) t = (int)vert.Y;
            if(vert.Y > b) b = (int)vert.Y+1;
            return new Rectangle(l-(int)cushion,t-(int)cushion,r-l+2+2*(int)(cushion+1.0f),b-t+2+2*(int)(cushion+1.0f));
        }
        public static Rectangle GetBoundingRect(Rectangle rect,float cushion)
        {
            int l = rect.Left;
            int r = rect.Right;
            int t = rect.Top;
            int b = rect.Bottom;
            return new Rectangle(l-(int)cushion,t-(int)cushion,r-l+2+2*(int)(cushion+1.0f),b-t+2+2*(int)(cushion+1.0f));
        }
        /// <summary>
        /// Creates an output RectangleF equal to the input RectangleF but expanded to include this VertexF and cushion.
        /// </summary>
        /// <param name="rf"></param>
        /// <param name="cushion"></param>
        /// <returns></returns>
        public RectangleF GetBoundingRectF(RectangleF rf,float cushion)
        {
            float l,t,r,b;
            l = rf.Left;
            r = rf.Right;
            t = rf.Top;
            b = rf.Bottom;
            if(X < l) l = X;
            if(X > r) r = X;
            if(Y < t) t = Y;
            if(Y > b) b = Y;
            return new RectangleF(l-cushion,t-cushion,r-l+1+2.0f*cushion,b-t+1+2.0f*cushion);
        }
        /// <summary>
        /// Computes the distance between two points.
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        public static float Distance(PointF p1,PointF p2)
        {
            return (float)Math.Sqrt(Math.Pow((double)(p1.X-p2.X),2.0)+Math.Pow((double)(p1.Y-p2.Y),2.0));
        }
        public static float Distance(VertexF p1,VertexF p2)
        {
            float dx,dy;
            dx = p2.X-p1.X;
            dy = p2.Y-p1.Y;
            return (float)Math.Sqrt(dx*dx+dy*dy);
        }
        public static float Distance(VertexF p1,PointF p2)
        {
            return (float)Math.Sqrt(Math.Pow((double)(p1.X-p2.X),2.0)+Math.Pow((double)(p1.Y-p2.Y),2.0));
        }
        public static float Distance(PointF p1,VertexF p2)
        {
            return (float)Math.Sqrt(Math.Pow((double)(p1.X-p2.X),2.0)+Math.Pow((double)(p1.Y-p2.Y),2.0));
        }
        public static float Angle(PointF basePt,PointF legPt)
        {
            float h = Distance(basePt,legPt);
            float output = (float)Math.Acos((double)((legPt.X-basePt.X)/h));
            if(legPt.Y-basePt.Y<0.0f) output = (float)(2.0*Math.PI)-output;		//	This line has a ">" sign because the screen coordinates are inverted along y-axis.
            return output % (float)(2.0*Math.PI);
        }
        public static float Angle(VertexF basePt,PointF legPt)
        {
            float h = Distance(basePt,legPt);
            float output = (float)Math.Acos((double)((legPt.X-basePt.X)/h));
            if(legPt.Y-basePt.Y<0.0f) output = (float)(2.0*Math.PI)-output;		//	This line has a ">" sign because the screen coordinates are inverted along y-axis.
            return output % (float)(2.0*Math.PI);
        }
        public static float Angle(PointF basePt,VertexF legPt)
        {
            float h = Distance(basePt,legPt);
            float output = (float)Math.Acos((double)((legPt.X-basePt.X)/h));
            if(legPt.Y-basePt.Y<0.0f) output = (float)(2.0*Math.PI)-output;		//	This line has a ">" sign because the screen coordinates are inverted along y-axis.
            return output % (float)(2.0*Math.PI);
        }
        public static double Angle(PointF leg1,PointF vert,PointF leg2)
        {
            double theta = (Angle(vert,leg1)-Angle(vert,leg2))%(2.0*Math.PI);
            if(theta > Math.PI)
                theta = 2.0*Math.PI-theta;
            return theta;
        }
        public static double Angle(VertexF leg1,VertexF vert,PointF leg2)
        {
            double theta = (Angle(vert,leg1)-Angle(vert,leg2))%(2.0*Math.PI);
            if(theta > Math.PI)
                theta = 2.0*Math.PI-theta;
            return theta;
        }
        public static Rectangle GetPtDrawRect(Point p,int radius)
        {
            return new Rectangle(p.X-radius,p.Y-radius,2*radius,2*radius);
        }
        public static Rectangle GetPtDrawRect(PointF p,int radius)
        {
            return new Rectangle((int)(p.X+0.5)-radius,(int)(p.Y+0.5)-radius,2*radius,2*radius);
        }
        public static Rectangle GetPtDrawRect(VertexF p,int radius)
        {
            return new Rectangle((int)(p.X+0.5)-radius,(int)(p.Y+0.5)-radius,2*radius,2*radius);
        }
        public static int NextInd(int length,int currentInd)
        {
            int output = currentInd+1;
            if(output > length-1) output = 0;
            return output;
        }
        public static int LastInd(int length,int currentInd)
        {
            int output = currentInd-1;
            if(output < 0) output = length-1;
            return output;
        }
        public static int NextIndInDirection(int length,int i1,int i2)
        {
            if(i2==LastInd(length,i1))	//	going backwards
                return LastInd(length,i1);
            return NextInd(length,i2);
        }
        public static int LastIndInDirection(int length,int i1,int i2)
        {
            return NextIndInDirection(length,i2,i1);
        }
        public static int InsertBetweenInd(int length,int i1,int i2)
        {
            if(i2==LastInd(length,i1))
                return i1;
            else return i2;
        }
        /// <summary>
        /// Computes the angle of the segment a-->b.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns>A value in the range [0, 2pi]</returns>
        public static double Angle(VertexF a,VertexF b)
        {
            double output =  Math.Atan2((double)(b.Y-a.Y),(double)(b.X-a.X));
            if(output<0.0)
                output+=Util.TwoPi;
            return output;
        }
        public int CompareTo(object obj)
        {
            if(this.GetHashCode() > obj.GetHashCode())
                return 1;
            else if(this.GetHashCode() < obj.GetHashCode())
                return -1;
            return 0;
        }
    }
}
